package com.yonyou.hxjy.controller;


import com.yonyou.hxjy.utils.Constants;
import com.yonyou.hxjy.utils.SM2Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpMessage;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.protocol.HTTP;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;

/**
 * @author: KeXue
 * @time: 2022/8/25
 * @description: SM2请求加解密实例
 */
@Slf4j
public class SM2Example {


    /**
     * 生成请求报文
     */
    public static HttpPost setupRequest(String requestData, String signEncryptionPrivateKey, String TARGET_URL,
                                        String bodyEncryptionKey, String token) {
        long timestamp = System.currentTimeMillis();

        // 请求数据拼接：  报文体+时间戳
        byte[] requestDataBytes = requestData.getBytes(StandardCharsets.UTF_8);
        byte[] timestampBytes = ("&timestamp=" + timestamp).getBytes(StandardCharsets.UTF_8);
        byte[] newBytes = new byte[requestDataBytes.length + timestampBytes.length];
        System.arraycopy(requestDataBytes, 0, newBytes, 0, requestDataBytes.length);
        System.arraycopy(timestampBytes, 0, newBytes, requestDataBytes.length, timestampBytes.length);
        // 生成签名
        byte[] signature = SM2Util.sign(signEncryptionPrivateKey, newBytes);
        String sign = Base64.encodeBase64String(SM2Util.encodeDERSignature(signature));
        log.info("签名:{}", sign);
        System.out.println("签名:{}"+sign);
        // 设置请求URL
        HttpPost httpPost = new HttpPost(TARGET_URL);
        // 请求头设置签名
        httpPost.setHeader(Constants.SIGN_HEADER_NAME, sign);

        // 请求头设置时间戳
        httpPost.setHeader(Constants.TIMESTAMP_HEADER, Long.toString(timestamp));
        // 请求头设置请求参数格式，请根据实际情况改写
        httpPost.setHeader(HTTP.CONTENT_TYPE, Constants.TARGET_CONTENT_TYPE);
        // 请求头设置TOKEN
        httpPost.setHeader(Constants.AUTHORIZATION, Constants.BEARER + token);

        // 报文体加密
        byte[] encryptedData = SM2Util.encrypt(bodyEncryptionKey, requestDataBytes);
        // 设置请求体
        httpPost.setEntity(new ByteArrayEntity(encryptedData));


        return httpPost;
    }

    /**
     * 处理响应报文
     */
    public static byte[] handleResponse(HttpResponse response, String bodyDecryptionKey) throws Exception {
        InputStream content = response.getEntity().getContent();
        byte[] responseData = IOUtils.toByteArray(content);

        if (responseData == null || responseData.length == 0) {
            return responseData == null ? new byte[0] : responseData;
        }

        // 步骤1 原始响应报文解密 如果服务网关获取加解密密钥失败，则无法解密请求报文，且无法加密响应报文。 这时候，网关会直接返回错误信息，响应报文是未加密状态。
        Boolean encryptionEnable = getHeader(response, Constants.ENCRYPTION_ENABLED_HEADER_NAME);

        if (Boolean.TRUE.equals(encryptionEnable)) {
            responseData = SM2Util.decrypt(bodyDecryptionKey, responseData);
        }

        Boolean xMbcloudCompress = getHeader(response, Constants.X_MBCLOUD_COMPRESS);
        if (Boolean.TRUE.equals(xMbcloudCompress)) {
            responseData = decompress(responseData);
        }

        return responseData;


    }

    public static Boolean getHeader(HttpMessage message, String name) {
        Header header = message.getFirstHeader(name);
        return header != null;
    }

    public static byte[] decompress(byte[] data) throws IOException {
        ByteArrayInputStream input = new ByteArrayInputStream(data);
        GZIPInputStream gzipInput = new GZIPInputStream(input);
        return IOUtils.toByteArray(gzipInput);
    }
}
